extern void dump_runq(u_char key, void *dev_id, struct pt_regs *regs);
extern void print_sched_histo(u_char key, void *dev_id, struct pt_regs *regs);
extern void reset_sched_histo(u_char key, void *dev_id, struct pt_regs *regs);
+#ifndef NDEBUG
+void reaudit_pages(u_char key, void *dev_id, struct pt_regs *regs);
+void audit_all_pages(u_char key, void *dev_id, struct pt_regs *regs);
+#endif
void initialize_keytable()
add_key_handler('r', dump_runq, "dump run queues");
add_key_handler('B', kill_dom0, "reboot machine gracefully");
add_key_handler('R', halt_machine, "reboot machine ungracefully");
- return;
+#ifndef NDEBUG
+ add_key_handler('m', reaudit_pages, "re-audit pages");
+ add_key_handler('M', audit_all_pages, "audit all pages");
+#endif
}
return err;
}
+
+
+#ifndef NDEBUG
+/*
+ * below are various memory debugging functions:
+ * __audit_page(): prints out all the ptes a pages is listed in
+ * audit_page(): in addition maintains a history of audited pages
+ * reaudit_pages(): re-audit previously audited pages
+ * audit_all_pages(): check the ref-count for all leaf pages
+ * also checks for zombie pages
+ *
+ * reaudit_page() and audit_all_pages() are designed to be
+ * keyhandler functions so that they can be easily invoked from the console.
+ */
+
+
+/*
+ * prints out all the pt's a page is listed in
+ */
+void __audit_page(unsigned long pfn) {
+ unsigned long i, j;
+ struct pfn_info *page;
+ unsigned long page_addr;
+ l1_pgentry_t *pl1e, l1e;
+
+ page = &frame_table[pfn];
+ page_addr = pfn << PAGE_SHIFT;
+
+ printk("audit page: pfn=%lx info: cf=%lx tf=%lx ts=%lx dom=%lx\n", pfn,
+ page->count_and_flags, page->type_and_flags,
+ page->tlbflush_timestamp, (unsigned long)page->u.domain);
+
+ /* walk the frame table */
+ for ( i = 0; i < max_page; i++ )
+ {
+ if ( (frame_table[i].count_and_flags & PGC_count_mask) == 0 )
+ continue;
+ if ( (frame_table[i].count_and_flags & PGC_zombie) != 0 )
+ continue;
+
+ /* check if entry is a page table (L1 page table) and in use */
+ if ( ((frame_table[i].type_and_flags & PGT_type_mask) ==
+ PGT_l1_page_table) &&
+ ((frame_table[i].type_and_flags & PGT_count_mask) != 0) )
+ {
+ pl1e = map_domain_mem(i << PAGE_SHIFT);
+
+ /* scan page table for page to audit */
+ for ( j=0; j < ENTRIES_PER_L1_PAGETABLE; j++ )
+ {
+ l1e = pl1e[j];
+ if ( l1_pgentry_empty(l1e) )
+ continue;
+ if ( l1_pgentry_to_pagenr(l1e) == pfn )
+ {
+ printk(" pte_pfn=%06lx cf=%08lx tf=%08lx dom=%08lx\n",
+ i, frame_table[i].count_and_flags,
+ frame_table[i].type_and_flags,
+ (unsigned long)frame_table[i].u.domain);
+ printk(" pte_idx=%03lx *pte_idx=%08lx\n",
+ j, l1_pgentry_val(l1e));
+ }
+ }
+ unmap_domain_mem(pl1e);
+ }
+ }
+
+}
+
+/*
+ * audit a page and keep a history of audited pfns
+ */
+#define LASTPAGES_SIZE 128
+static long last_pages[LASTPAGES_SIZE];
+static int last_pages_idx = 0;
+void audit_page(unsigned long pfn)
+{
+ unsigned long i;
+
+ cli();
+ __audit_page(pfn);
+ sti();
+ /* add pfn to last_pages cache if is not already present */
+ for ( i = 0; i < LASTPAGES_SIZE; i++ )
+ if ( last_pages[i] == pfn )
+ return;
+
+ /* new entry */
+ last_pages[last_pages_idx++] = pfn;
+ if ( last_pages_idx >= LASTPAGES_SIZE )
+ last_pages_idx = 0;
+
+}
+
+/*
+ * re-audit previously audited pages
+ */
+void reaudit_pages(u_char key, void *dev_id, struct pt_regs *regs)
+{
+ int i;
+
+ printk("Dumping audited pages\n");
+
+ for ( i = 0; i < LASTPAGES_SIZE; i++ )
+ if ( last_pages[i] != 0 )
+ __audit_page(last_pages[i]);
+}
+
+/*
+ * do various checks on all pages.
+ * Currently:
+ * - check for zombie pages
+ * - check for pages with corrupt ref-count
+ * Interrupts are diabled completely. use with care.
+ */
+void audit_all_pages (u_char key, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long i, j, k;
+ unsigned long ref_count;
+ l1_pgentry_t *pl1e, l1e;
+
+ printk("audit_all_pages\n");
+
+ cli();
+
+ /* walk the frame table */
+ for ( i = 0; i < max_page; i++ )
+ {
+
+ /* check for zombies */
+ if ( ((frame_table[i].count_and_flags & PGC_count_mask) != 0) &&
+ ((frame_table[i].count_and_flags & PGC_zombie) != 0) )
+ {
+ printk("zombie: pfn=%08lx cf=%08lx tf=%08lx dom=%08lx\n",
+ i, frame_table[i].count_and_flags,
+ frame_table[i].type_and_flags,
+ (unsigned long)frame_table[i].u.domain);
+ }
+
+ /* check ref count for leaf pages */
+ if ( ((frame_table[i].type_and_flags & PGT_type_mask) ==
+ PGT_writeable_page) )
+ {
+ ref_count = 0;
+
+ /* find page tables */
+ for ( j = 0; j < max_page; j++ )
+ {
+ if ( ((frame_table[j].type_and_flags & PGT_type_mask) ==
+ PGT_l1_page_table) &&
+ ((frame_table[j].type_and_flags & PGT_count_mask) != 0) )
+ {
+ pl1e = map_domain_mem(j << PAGE_SHIFT);
+
+ /* scan page table for page to audit */
+ for ( k=0; k < ENTRIES_PER_L1_PAGETABLE; k++ )
+ {
+ l1e = pl1e[k];
+ if ( l1_pgentry_empty(l1e) )
+ continue;
+ if ( l1_pgentry_to_pagenr(l1e) == i )
+ {
+ ref_count++;
+ /* page is in pagetable */
+ }
+ }
+ unmap_domain_mem(pl1e);
+ }
+
+ }
+
+ /* check for PGC_ALLOCATED */
+ if ( (frame_table[i].count_and_flags & PGC_allocated) != 0 )
+ ref_count++;
+
+ if ( (frame_table[i].count_and_flags & PGC_count_mask)
+ != ref_count )
+ {
+ printk("refcount error: pfn=%06lx cf=%08lx refcount=%lx\n",
+ i, frame_table[i].count_and_flags, ref_count);
+ __audit_page(i);
+ printk("\n");
+ }
+ } /* ref count error */
+ }
+ sti();
+
+}
+
+#endif /* NDEBUG */